package JET.editor;

import JET.Centers.ControlCenter;
import JET.SA.SA_Selection;
import JET.Scene;
import JET.SceneHelper;
import JET.StaticActions;
import JET.geom.Vec2d;
import java.util.ArrayList;
import JET.physics.Mass;
import JET.physics.RSpring;
import JET.physics.Spring;
import JET.physics.bodies.SpringBody;
import JET.physics.Wall;
import java.util.Collection;
import java.util.Collections;

/**
 *
 * @author 057Ry
 */
public class SelectionGroup implements Cloneable
{
    static final ControlCenter CC = ControlCenter.getInstance();
    private Scene scene;

    protected final ArrayList<Mass> massList = new ArrayList<Mass>();
    protected final Collection<Mass> massListSync  = Collections.synchronizedList(massList);
    protected final ArrayList<Spring> springList = new ArrayList<Spring>();
    protected final Collection<Spring> springListSync  = Collections.synchronizedList(springList);
    protected final ArrayList<RSpring> rSpringList = new ArrayList<RSpring>();
    protected final Collection<RSpring> rSpringListSync  = Collections.synchronizedList(rSpringList);
    protected final ArrayList<Wall> wallList = new ArrayList<Wall>();
    protected final Collection<Wall> wallListSync  = Collections.synchronizedList(wallList);

    private boolean massesInOrder;

    private final Vec2d center = new Vec2d();
    private boolean needToRecalcCenter = true;

    public SelectionGroup(Scene scene)
    {
        this.scene = scene;

//        massList = new ArrayList<Mass>();
//        springList = new ArrayList<Spring>();
//        rSpringList = new ArrayList<RSpring>();
//        wallList = new ArrayList<Wall>();
    }

    public Scene getScene() {
        return scene;
    }

    public boolean isMassesInOrder() {
        return massesInOrder;
    }

    public void setMassesInOrder(boolean massesInOrder) {
        this.massesInOrder = massesInOrder;
    }

    public ArrayList<Mass> getMassList() {
        return massList;
    }

    public ArrayList<Spring> getSpringList() {
        return springList;
    }

    public ArrayList<RSpring> getRSpringList() {
        return rSpringList;
    }

    public ArrayList<Wall> getWallList() {
        return wallList;
    }

    public Collection<Mass> getMassListSync() {
        return massListSync;
        //return Collections.synchronizedList(massList);
    }

    public Collection<Spring> getSpringListSync() {
        return springListSync;
        //return Collections.synchronizedList(springList);
    }

    public Collection<RSpring> getRSpringListSync() {
        return rSpringListSync;
        //return Collections.synchronizedList(rSpringList);
    }

    public Collection<Wall> getWallListSync() {
        return wallListSync;
        //return Collections.synchronizedList(wallList);
    }

    public void clear() {
        getMassList().clear();
        getSpringList().clear();
        getRSpringList().clear();
        getWallList().clear();
        needToRecalcCenter = true;
    }
    
    public Object Clone() {
        SelectionGroup g = new SelectionGroup(scene);

        g.addMasses(massList);
        g.addSprings(springList);
        g.addRSprings(rSpringList);
        g.addWalls(wallList);

        g.massesInOrder = this.massesInOrder;
        g.center.become(this.center);
        g.needToRecalcCenter = this.needToRecalcCenter;

        return g;
    }
    
    public boolean isEmpty() {
        return getMassList().isEmpty() && getSpringList().isEmpty() &&
               getRSpringList().isEmpty() && getWallList().isEmpty();
    }

    public void addMass(Mass mass) {
        synchronized(massListSync) {
            if (!massListSync.contains(mass))
                massListSync.add(mass);
        }
        needToRecalcCenter = true;
    }
    public void addMasses(Collection<Mass> masses) {
        synchronized(massListSync) {
            massListSync.addAll(masses);
        }
        needToRecalcCenter = true;
    }

    public void addSpring(Spring spring) {
        synchronized(springListSync) {
            if (!springListSync.contains(spring))
                springListSync.add(spring);
        }
    }
    public void addSprings(Collection<Spring> springs) {
        synchronized(springListSync) {
            springListSync.addAll(springs);
        }
    }

    public void addRSpring(RSpring rSpring) {
        synchronized(rSpringListSync) {
            if (!rSpringListSync.contains(rSpring))
                rSpringListSync.add(rSpring);
        }
    }
    public void addRSprings(Collection<RSpring> rSprings) {
        synchronized(rSpringListSync) {
            rSpringListSync.addAll(rSprings);
        }
    }

    public void addWall(Wall wall) {
        synchronized(wallListSync) {
            if (!wallListSync.contains(wall))
                wallListSync.add(wall);
        }
    }
    public void addWalls(Collection<Wall> walls) {
        synchronized(wallListSync) {
            wallListSync.addAll(walls);
        }
    }

    public void removeMass(Mass mass) {
        synchronized(massListSync) {
            massListSync.remove(mass);
        }
        needToRecalcCenter = true;
    }

    public void removeMasses(Collection<Mass> masses) {
        synchronized(massListSync) {
            massListSync.removeAll(masses);
        }
        needToRecalcCenter = true;
    }

    public void removeSpring(Spring spring) {
        synchronized(springListSync) {
            springListSync.remove(spring);
        }
    }
    public void removeSprings(ArrayList<Spring> springs) {
        synchronized(springListSync) {
            springListSync.removeAll(springs);
        }
    }

    public void removeRSpring(RSpring rSpring) {
        synchronized(rSpringListSync) {
            rSpringListSync.remove(rSpring);
        }
    }
    public void removeRSprings(ArrayList<RSpring> rSprings) {
        synchronized(rSpringListSync) {
            rSpringListSync.removeAll(rSprings);
        }
    }

    public void removeWall(Wall wall) {
        synchronized(wallListSync) {
            wallListSync.remove(wall);
        }
    }
    public void removeWalls(ArrayList<Wall> walls) {
        synchronized(wallListSync) {
            wallListSync.removeAll(walls);
        }
    }

    public Vec2d getMassCenterVec() {
        // TODO : get rid of allways true boolean shit ta daa...
        if (needToRecalcCenter || true)
        {
            center.zero();
            synchronized(massListSync) {
                for(Mass mass : massListSync)
                    center.add(mass.getPosition());
                center.scale(1./massListSync.size());
            }
            needToRecalcCenter = false;
        }

        return center;
    }

    /**
     * Generates a list of all Spring Bodies being part of selection.
     * Even if only single mass of a spring body is contained in selection, this spring body
     * is placed in returning list.
     *
     * @return  List of all spring bodies that are involved in selection
     *          (If no spring body is selected, list is returned but it's empty).
     */
    public ArrayList<SpringBody> getSpringBodies() {
        ArrayList<SpringBody> list = new ArrayList<SpringBody>();

        synchronized(massListSync) {
            for(Mass m : massListSync) {
                SpringBody sb = SceneHelper.getSpringBodyThatOwnMass(scene, m);
                if (!list.contains(sb))
                    list.add(sb);
            }
        }
        
        return list;
    }

    /**
     * Generates a list of all Spring Bodies being fully selected
     * (all masses of spring body are part of selection).
     *
     * @return  List of all spring bodies that are fully involved in selection
     *          (If no spring body is fully selected, list is returned but it's empty).
     */
    public ArrayList<SpringBody> getSpringBodiesFull() {
        ArrayList<SpringBody> list = new ArrayList<SpringBody>();
        ArrayList<SpringBody> ignoredList = new ArrayList<SpringBody>();
        
    iterate_selection_masses:
        for(Mass m : massList) {
            SpringBody sb = SceneHelper.getSpringBodyThatOwnMass(scene, m);

            // If we already know that this spring body isn't fully included in selection - continue
            if (ignoredList.contains(sb))
                continue;

            synchronized(massListSync) {
                for(Mass sbMass : massListSync)
                    if (massListSync.contains(sbMass))
                        continue;
                    else // Add spring body to ignored list, and go for another selection mass...
                    {
                        if (!ignoredList.contains(sb))
                            ignoredList.add(sb);
                        continue iterate_selection_masses;
                    }
            }

            if (!list.contains(sb))
                list.add(sb);
        }

        return list;
    }

    /**
     * Generates list of masses that are included in selection and are masses of
     * passes spring body.
     *
     *  @param sb Spring body that masses we are looking for in selection.
     *  @return List of masses. If no masses are sb's masses, or no sb's masses are in selection,
     *          returned list is empty.
     */
    public ArrayList<Mass> getSpringBodyMasses(SpringBody sb) {
        ArrayList<Mass> list = new ArrayList<Mass>();

        synchronized(massListSync) {
            for(Mass m : massListSync) {
                if (sb.getMassListSync().contains(m))
                    list.add(m);
            }
        }
        
        return list;
    }

    /**
     * Spreads selection on items having conatact with any mass of any partially selected spring body.
     * @param onEngine If true all engine items (springs and rsprings) will be taken under cosideretion wheather add them to selection or not.
     * @param onSpringBodies If true all spring bodies items (springs, rsprings and masses) will be taken under cosideretion wheather add them to selection or not.
     */
    public void spreadSelection(boolean onEngine, boolean onSpringBodies, boolean onSceneItems)
    {
        ArrayList<SpringBody> sbList = getSpringBodies();

        for(SpringBody sb : sbList)
        {
            synchronized(massListSync)
            {
                for(Mass m : massListSync)
                {
                    ArrayList<Spring> springList = SceneHelper.getSpringListOfMass(scene, m, onEngine, onSpringBodies, onSceneItems);
                    for(Spring s : springList)
                        //this.addSpring(s);
                        SA_Selection.addSpringToSelection(s, scene, true);

                    ArrayList<RSpring> rSpringList = SceneHelper.getRSpringListOfMass(scene, m, onEngine, onSpringBodies, onSceneItems);
                    for(RSpring s : rSpringList)
                        //this.addRSpring(s);
                        SA_Selection.addRSpringToSelection(s, scene, true);

                    // Also try to add this sb mass if user wants
                    if (onSpringBodies)
                        //this.addMass(m);
                        SA_Selection.addMassToSelection(m, scene, true);
                }
            }
        }
    }
}
